#ifndef GAMEENGINE_CORE_RTTI_H
#define GAMEENGINE_CORE_RTTI_H

#include <GameEngine/_defs.h>
#include <GameEngine/Core/core_settings.h>

#include <GameEngine/Core/string.h>
#include <GameEngine/Core/Macros/macro_singleton.h>

#include <vector>
#include <typeinfo>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif



//! Define RTTI variable for a single class
#define GAMEENGINE_DECLARE_RTTI_VARIABLE(class_name)\
	private:\
		static int GameEngine::class_name::rtti_id_;


//! Add RTTI support for a single class
/*!
 *  If the class is derived from a class PARENT,
 *  calling the is_a function on the parent will
 *  return false. Code example:
 *  @code
 *  CHILD c;<br>
 *  bool test = c.is_a<PARENT>(); // test is false
 *  @endcode
 */
#define GAMEENGINE_DECLARE_RTTI_SINGLE(class_name)\
	GAMEENGINE_DECLARE_RTTI_VARIABLE(class_name)\
	\
	public:\
		/*! \brief Accessor to the rtti value associated to class class_name */ \
		static int rtti();\
		\
		/*! \brief Test if class class_name is the same as class T. */ \
		/*! \details If the class class_name is derived from a class PARENT, calling the is_a function on the parent will return false. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<PARENT>(); // test is false \endcode */ \
		/*! \details To get a different behavior, please use the GAMEENGINE_DECLARE_RTTI_PARENT macro. */ \
		template <class T> static bool is_a() {\
			return ( typeid(T) == typeid(class_name) );\
		}\
		\
		/*! \brief Test if class class_name is the same as class T. */ \
		/*! \details If the class class_name is derived from a class PARENT, calling the is_a function on the parent will return false. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<PARENT>(); // test is false \endcode */ \
		/*! \details To get a different behavior, please use the GAMEENGINE_DECLARE_RTTI_PARENT macro. */ \
		template <class T> static bool is_a(const T& item) {\
			return class_name::is_a<T>();\
		}\
		\
		/*! \brief Test if class class_name is the same as class type info. */ \
		/*! \details If the class class_name is derived from a class PARENT, calling the is_a function on the parent will return false. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a( typeid(PARENT) ); // test is false \endcode */ \
		/*! \details To get a different behavior, please use the GAMEENGINE_DECLARE_RTTI_PARENT macro. */ \
		/*! \param info The type info to test. */ \
		static bool is_a(const std::type_info& info) {\
			return ( typeid(class_name) == info );\
		}


//! Add RTTI support for a class with parent recognition
/*!
 *  If the class is derived from a class PARENT,
 *  calling the is_a function on the parent will
 *  return true. Code example:
 *  @code
 *  CHILD c;
 *  bool test = c.is_a<PARENT>(); // test is true
 *  @endcode
 *
 *  If class PARENT inherits a class GRANDPARENT, the child
 *  class may or may not be recognized as belonging to the
 *  GRANDPARENT type, depending on the RTTI support of class
 *  PARENT. Code example:
 *  @code
 *  CHILD c;
 *  bool test = c.is_a<GRANDPARENT>();
 *  // test is equal to PARENT::is_a<GRANDPARENT>()
 *  @endcode
 *
 @note Parent class should have RTTI support.
 */
#define GAMEENGINE_DECLARE_RTTI_PARENT(class_name,parent_class)\
	GAMEENGINE_DECLARE_RTTI_VARIABLE(class_name)\
	\
	public:\
		/*! \brief Accessor to the rtti value associated to class class_name */ \
		static int rtti();\
		\
		/*! \brief Test if class class_name is the same as class T, or derives from class T (its direct parent is parent_class). */ \
		/*! \details If the class is derived from a class PARENT, calling the is_a function on the parent will return true. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<PARENT>(); // test is true \endcode */ \
		/*! \details If class PARENT inherits a class GRANDPARENT, the child class may or may not be recognized as belonging to the GRANDPARENT type, depending on the RTTI support of class PARENT. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<GRANDPARENT>(); // test is equal to PARENT::is_a<GRANDPARENT>() \endcode */ \
		template <class T> static bool is_a() {\
			if ( typeid(T) == typeid(class_name) ) return true;\
			return parent_class::is_a<T>();\
		}\
		\
		/*! \brief Test if class class_name is the same as class T, or derives from class T (its direct parent is parent_class). */ \
		/*! \details If the class is derived from a class PARENT, calling the is_a function on the parent will return true. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<PARENT>(); // test is true \endcode */ \
		/*! \details If class PARENT inherits a class GRANDPARENT, the child class may or may not be recognized as belonging to the GRANDPARENT type, depending on the RTTI support of class PARENT. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a<GRANDPARENT>(); // test is equal to PARENT::is_a<GRANDPARENT>() \endcode */ \
		template <class T> static bool is_a(const T& item) {\
			return class_name::is_a<T>();\
		}\
		\
		/*! \brief Test if class class_name is the same as class type info, or derives from class type info (its direct parent is parent_class). */ \
		/*! \details If the class is derived from a class PARENT, calling the is_a function on the parent will return true. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a( typeid(PARENT) ); // test is true \endcode */ \
		/*! \details If class PARENT inherits a class GRANDPARENT, the child class may or may not be recognized as belonging to the GRANDPARENT type, depending on the RTTI support of class PARENT. Code example: */ \
		/*! \code CHILD c; bool test = c.is_a( typeid(GRANDPARENT) ); // test is equal to PARENT::is_a( typeid(GRANDPARENT) ) \endcode */ \
		/*! \param info The type info to test. */ \
		static bool is_a(const std::type_info& info) {\
			if ( typeid(class_name) == info ) return true;\
			return parent_class::is_a(info);\
		}


//! Implement RTTI variable for a single class
#define GAMEENGINE_IMPLEMENT_RTTI_VARIABLE(class_name)\
	/*! \brief Rtti value associated to class class_name */ \
	int GameEngine::class_name::rtti_id_ = GameEngine::RttiManager::instance()->find_or_assign_rtti( typeid( class_name ).name() );


//! Implement the RTTI support for a class
/*!
 *  This macro is common for all types of RTTI support
 */
#ifndef DOXYGEN_IGNORE
#define GAMEENGINE_IMPLEMENT_RTTI(class_name)\
	GAMEENGINE_IMPLEMENT_RTTI_VARIABLE(class_name)\
	\
	int class_name::rtti() {\
		return rtti_id_;\
	}
#else
#define GAMEENGINE_IMPLEMENT_RTTI(class_name)
#endif


//=====================================================================
//=====================================================================
//=====================================================================


//! Internal class in charge of providing RTTI IDs
/*!
 *  RTTI stands for Run-Time Type Information. It
 *  aims at finding the type of an item while the
 *  program is running.
 *
 *  The RTTI is here coded as an integer, which is
 *  provided at run-time when a class requires a
 *  RTTI support. This integer can then be used for
 *  type checking. You can use the RTTI macros
 *  GAMEENGINE_DECLARE_RTTI* and GAMEENGINE_IMPLEMENT_RTTI
 *  in order to implement safe type checking in your
 *  classes.
 *
 @note Since the RTTI type is provided at run-time,
       there is no guarantee that the RTTI of a given
	   class will be coded with the same integer if
	   program is run twice. The RTTI integer should
	   thus not be used to save information that should
	   persist after the program is killed.
 @note The RTTI integer may change if RTTI support is
       used on different DLLs or programs. To avoid
	   type checking errors, it is highly recommanded
	   to use the predefined macros rather than doing
	   a direct integer comparison.
 */
class _Core_decl RttiManager {

GAMEENGINE_DECLARE_SINGLETON(RttiManager)

public:
	virtual ~RttiManager();

	int find_or_assign_rtti(const GEString& name);
	int assign_rtti(const GEString& name);
	int find_rtti(const GEString& name) const;

	int rtti_classes_count() const;

private:
	RttiManager();

private:
	//! Current rtti slot
	int rtti_id_;

	//! Assigned classes
	GAMEENGINE_FORCE_DLL_CLASS_STD(vector,GEString) classes_;
};

//=====================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#endif
